/*
 * Ingenic CU1000-Neo setup code
 *
 * Copyright (c) 2013 Ingenic Semiconductor Co.,Ltd
 * Author: Zoro <ykli@ingenic.cn>
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */

#include <common.h>
#include <environment.h>
#include <netdev.h>
#include <regulator.h>
#include <asm/arch/jzsoc.h>
#include <asm/jz_mmc.h>
#include <u-boot/sha1.h>

#define REG32(addr) *(volatile unsigned int *)(addr)
#define EFUSE_CTRL	    0xb3540000
#define EFUSE_CFG	    0xb3540004
#define EFUSE_STATE	    0xb3540008
#define EFUSE_DATA0	    0xb354000C
#define EFUSE_DATA1	    0xb3540010
#define EFUSE_DATA2	    0xb3540014
#define EFUSE_DATA3	    0xb3540018

extern int jz_net_initialize(bd_t *bis);

#ifdef CONFIG_BOARD_EARLY_INIT_F
int board_early_init_f(void)
{
	void __iomem *gpio_regs = (void __iomem *)GPIO_BASE;
	void __iomem *cpm_regs = (void __iomem *)CPM_BASE;

	regulator_init();
	regulator_set_voltage(REGULATOR_CORE, CONFIG_SPL_CORE_VOLTAGE);

	/* switch SCLKA to EXCLK */
	writel((readl(cpm_regs + CPM_CPCCR) & 0x3fffffff) | (1 << 30), cpm_regs + CPM_CPCCR);
	while(!(readl(cpm_regs + CPM_CPCSR) & (1 << 31)));

	/* reconfigure APLL frequency */
	writel(CONFIG_SYS_APLL_MNOD | (1 << 8) | 0x20, cpm_regs + CPM_CPAPCR);
	while(!(readl(cpm_regs + CPM_CPAPCR) & (1 << 10)));

	/* switch SCLKA back to APLL */
	writel((readl(cpm_regs + CPM_CPCCR) & 0x3fffffff) | (2 << 30), cpm_regs + CPM_CPCCR);
	while(!(readl(cpm_regs + CPM_CPCSR) & (1 << 31)));

	/* disable pull for all pins */
	writel(0xffffffff, gpio_regs + GPIO_PXPEN(0));
	writel(0xffffffff, gpio_regs + GPIO_PXPEN(1));
	writel(0xffffffff, gpio_regs + GPIO_PXPEN(2));
	writel(0xffffffff, gpio_regs + GPIO_PXPEN(3));

	/* setup UART2 pins */
	writel(0x00000030, gpio_regs + GPIO_PXINTC(3));
	writel(0x00000030, gpio_regs + GPIO_PXMASKC(3));
	writel(0x00000030, gpio_regs + GPIO_PXPAT1C(3));
	writel(0x00000030, gpio_regs + GPIO_PXPAT0C(3));
	writel(0x00000030, gpio_regs + GPIO_PXPENC(3));

	return 0;
}
#endif

static void read_efuse_segment(unsigned int addr, unsigned int length, unsigned int *buf)
{
	unsigned int val;

	/* clear read done staus */
	REG32(EFUSE_STATE) = 0;
	val = addr << 21 | length << 16 | 1;
	REG32(EFUSE_CTRL) = val;
	/* wait read done status */
	while(!(REG32(EFUSE_STATE) & 1));

	buf[0] = REG32(EFUSE_DATA0);
	buf[1] = REG32(EFUSE_DATA1);
	buf[2] = REG32(EFUSE_DATA2);
	buf[3] = REG32(EFUSE_DATA3);

	/* clear read done staus */
	REG32(EFUSE_STATE) = 0;
}

int misc_init_r(void)
{
	void __iomem *cpm_regs = (void __iomem *)CPM_BASE;
	uint32_t chipid[4] = { 0 };
	uint8_t macaddr[20] = { 0 };

	writel(readl(cpm_regs + CPM_CLKGR) & ~CPM_CLKGR_EFUSE, cpm_regs + CPM_CLKGR);
	read_efuse_segment(0x00, 15, chipid);
	writel(readl(cpm_regs + CPM_CLKGR) | CPM_CLKGR_EFUSE, cpm_regs + CPM_CLKGR);

	sha1_csum((uint8_t *)chipid, sizeof(chipid), macaddr);

	macaddr[0] &= 0xfe;

	/* set MAC address */
	eth_env_set_enetaddr("ethaddr", macaddr);

	return 0;
}

#ifdef CONFIG_MMC
int board_mmc_init(bd_t *bd)
{
	void __iomem *gpio_regs = (void __iomem *)GPIO_BASE;
	void __iomem *cpm_regs = (void __iomem *)CPM_BASE;
	uint32_t msc_cdr;

	/* setup MSC0 clock */
	msc_cdr = CONFIG_SYS_MPLL_FREQ / 50000000 / 2 - 1;
	writel(readl(cpm_regs + CPM_CLKGR) & ~CPM_CLKGR_MSC0, cpm_regs + CPM_CLKGR);
	writel(msc_cdr | CPM_MSCCDR_MPCS_MPLL | CPM_MSCCDR_CE, cpm_regs + CPM_MSC0CDR);
	while (readl(cpm_regs + CPM_MSC0CDR) & CPM_MSCCDR_MSC_BUSY);

	/* setup MSC0 pins */
	writel(0x03ff0000, gpio_regs + GPIO_PXINTC(0));
	writel(0x03ff0000, gpio_regs + GPIO_PXMASKC(0));
	writel(0x03ff0000, gpio_regs + GPIO_PXPAT1C(0));
	writel(0x03ff0000, gpio_regs + GPIO_PXPAT0S(0));

	jz_mmc_init((void __iomem *)MSC0_BASE);

	return 0;
}
#endif

#ifdef CONFIG_SYS_NAND_SELF_INIT
void board_nand_init(void)
{
	return 0;
}
#endif

int board_eth_init(bd_t *bis)
{
	void __iomem *gpio_regs = (void __iomem *)GPIO_BASE;
	void __iomem *cpm_regs = (void __iomem *)CPM_BASE;
	int rv;
#ifndef  CONFIG_USB_ETHER
	/* reset grus DM9000 */
#ifdef CONFIG_NET_GMAC
	uint32_t mac_cdr;

	/* setup MAC clock */
	mac_cdr = CONFIG_SYS_MPLL_FREQ / 50000000 - 1;
	writel(readl(cpm_regs + CPM_CLKGR) & ~CPM_CLKGR_MAC, cpm_regs + CPM_CLKGR);
	writel(mac_cdr | CPM_MACCDR_MACPCS_MPLL | CPM_MACCDR_CE, cpm_regs + CPM_MACCDR);
	while (readl(cpm_regs + CPM_MACCDR) & CPM_MACCDR_MAC_BUSY);

	/* setup MAC pins */
	writel(0x0000ffc0, gpio_regs + GPIO_PXINTC(1));
	writel(0x0000ffc0, gpio_regs + GPIO_PXMASKC(1));
	writel(0x0000ffc0, gpio_regs + GPIO_PXPAT1C(1));
	writel(0x0000ffc0, gpio_regs + GPIO_PXPAT0S(1));

	gpio_direction_output(GPIO_PC(23), 0);
	udelay(10000);
	gpio_direction_output(GPIO_PC(23), 1);
	udelay(10000);

	rv = jz_net_initialize(bis);
#endif
#else
	rv = usb_eth_initialize(bis);
#endif

	return rv;
}

#ifdef CONFIG_SPL_NOR_SUPPORT
int spl_start_uboot(void)
{
	return 1;
}
#endif

#ifdef CONFIG_DISPLAY_BOARDINFO
/* U-Boot common routines */
int checkboard(void)
{
	puts("Board: CU1000-Neo (Ingenic XBurst X1000E SoC)\n");

	(*(volatile unsigned int *)0xB0010718) = 1 << 10;
	(*(volatile unsigned int *)0xB0010724) = 1 << 10;
	(*(volatile unsigned int *)0xB0010738) = 1 << 10;
	(*(volatile unsigned int *)0xB0010744) = 1 << 10;
	(*(volatile unsigned int *)0xB00107F0) = 0x0;
	puts("Force Pull-up PA10 for eMMC RSTN Signal\n");

	return 0;
}
#endif

#ifdef CONFIG_SPL_BUILD
void spl_board_init(void)
{
}
#endif /* CONFIG_SPL_BUILD */
